#pragma once
#include<iostream>
#include<memory>

using namespace std;

namespace tao {

template<class T>
class auto_ptr {
public:
	auto_ptr(T* ptr)
		:_ptr(ptr)
	{}

	~auto_ptr() {
		cout << "delete" << _ptr << endl;
		delete _ptr;
	}

	T& operator*() {
		return *_ptr;
	}

	T* operator->() {
		return _ptr;
	}

	auto_ptr(auto_ptr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = nullptr;
	}
protected:
	T* _ptr;
};

template<class T>
class unique_ptr {
public:
	unique_ptr(T* ptr)
		:_ptr(ptr)
	{}

	unique_ptr(unique_ptr<T>& up) = delete;
	unique_ptr<T>& operator=(unique_ptr<T>& up) = delete;

	~unique_ptr(){
		cout << "delete" << _ptr << endl;
		delete _ptr;
	}

	T& operator*() {
		return *_ptr;
	}

	T* operator->() {
		return _ptr;
	}


protected:
	T* _ptr;
};

template<class T>
class shared_ptr {
public:
	shared_ptr(T* ptr=nullptr)
		:_ptr(ptr)
		,_pcnt(new int(1))
	{}

	T& operator*() {
		return *_ptr;
	}

	T* operator->() {
		return _ptr;
	}

	~shared_ptr() {
		if (--*_pcnt == 0) {
			cout << "delete:" << _ptr << endl;
			delete _ptr;
			delete _pcnt;
		}
	}

	shared_ptr(const shared_ptr<T>& sp)
		:_ptr(sp._ptr)
		, _pcnt(sp._pcnt)
	{
		(*_pcnt)++;
	}

	shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
		if (this == &sp) return *this;
		if (--*_pcnt == 0) {
			delete _ptr;
			delete _pcnt;
		}
		_ptr = sp._ptr;
		_pcnt = sp._pcnt;
		*_pcnt++;
		return *this;
	}

	int use_count()const {
		return *_pcnt;
	}

	T* get()const {
		return _ptr;
	}

protected:
	T* _ptr;
	int* _pcnt;
};

template<class T>
class weak_ptr {
public:
	weak_ptr()
		:_ptr(nullptr)
	{}

	weak_ptr(const shared_ptr<T>& sp)
		:_ptr(sp.get())
	{}

	weak_ptr<T>& operator=(const shared_ptr<T>& sp) {
		_ptr = sp.get();
		return *this;
	}

	T* operator->() {
		return _ptr;
	}

	T& operator*() {
		return *_ptr;
	}
protected:
	T* _ptr;
};

}